package com.yyq.sos.util.fileupload;


import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang.StringUtils;

import com.yyq.sos.util.DateUtil;

/**
 * 文件上传处理类
 * 通过该处理文件上传，并返回成功上传的文件。
 */
public class FileUploadHandle {
	
	public final static String NO="no";
	public final static String YES="yes";
	public final static String AUTO="auto";
	
	//允许上传的文件类型
	private String allowedTypes;
	
	//单个文件大小（单位：KB）
	private long maxSize;
	
	//源文件
	private File srcFile;
	
	//源文件名
	private String srcFileName;
	
	//目标文件名
	private String destFileName;
	
	//配制的上传文件目录，有可能是绝对路径，也有可能是相对路径
	private String uploadDir;
	
	//系统根据uploadDir解析绝对路径。
	private String realPathDir;
	
	
	//文件后缀
	private String suffix;
	
	private HttpServletRequest request;
	
	//设置文件名策略（NO{不改变文件名，如果有重复，覆盖老的} 
	//AUTO{如果有重复则重命名[不推荐使用]} 
	//YES{系统随机生成文件名}）
	private String needChangeFileName=NO;
	
	
	/**
	 * 源文件
	 * @param srcFile
	 */
	public void setSrcFile(File srcFile) {
		this.srcFile = srcFile;
	}

	/**
	 * 源文件名称
	 * @param srcFileName
	 */
	public void setSrcFileName(String srcFileName) {
		this.srcFileName = srcFileName;
	}

	/**
	 * 上传目录
	 * @param uploadDir
	 */
	public void setUploadDir(String uploadDir) {
		this.uploadDir = uploadDir;
	}

	
	public void setRequest(HttpServletRequest request) {
		this.request = request;
	}

	/**
	 * 设置是否需要修改源文件名（为了文件名重复，一般都需要修改）
	 * @param needChangeFileName
	 */
	public void setNeedChangeFileName(String needChangeFileName) {
		this.needChangeFileName = needChangeFileName;
	}
	
	/**
	 * 处理上传文件 如果需要上传，只要在action中调用此方法，但页面表单文件域名称必须为files
	 * 调用此方法结束后，返回一个存放所有上传文件的集合。
	 * 
	 * @return
	 */
	public List<FileBean> handleUploadFile(List<File> files,List<String> filesFileName,String fileAllowedTypes,Long fileMaxSize) {
		List<FileBean> fileList = new ArrayList<FileBean>();
		try {
			if(files!=null){
				for (int i = 0; i < files.size(); i++) {
					FileUploadHandle uploadHandle = new FileUploadHandle();
					uploadHandle.setNeedChangeFileName("yes");
					uploadHandle.setRequest(request);
					uploadHandle.setSrcFile(files.get(i));
					if(filesFileName==null){
						return fileList;
					}
					uploadHandle.setSrcFileName(filesFileName.get(i));
					uploadHandle.setUploadDir(uploadDir);
					uploadHandle.setAllowedTypes(fileAllowedTypes);
					uploadHandle.setMaxSize(fileMaxSize);
					uploadHandle.upload();
					FileBean fileBean = new FileBean();
					fileBean.setNewName(uploadHandle.getDestFileName());
					fileBean.setOldName(uploadHandle.getSrcFileName());
					long sizefil = uploadHandle.getSrcFile().length()/1024;
					fileBean.setSize(sizefil == 0 ? 1 : sizefil);
					fileBean.setSuffix(uploadHandle.getSuffix());
					fileBean.setUrl(uploadHandle.getRealPathDir() + uploadHandle.getDestFileName());
					fileList.add(fileBean);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
			// 将文件上传的异常传换成应用异常。
			throw new RuntimeException(e);
		}
		return fileList;
	}
	
	/**
	 * 处理上传文件 如果需要上传，只要在action中调用此方法，但页面表单文件域名称必须为files
	 * 调用此方法结束后，返回一个存放所有上传文件的集合。
	 * 
	 * @return
	 */
	public FileBean handleUploadFile(File file,String filesFileName,String fileAllowedTypes,Long fileMaxSize) {
		FileBean fileBean = new FileBean();
		try {
			if(file!=null){
				FileUploadHandle uploadHandle = new FileUploadHandle();
				uploadHandle.setNeedChangeFileName("yes");
				uploadHandle.setRequest(request);
				uploadHandle.setSrcFile(file);
				if(filesFileName==null){
					return null;
				}
				uploadHandle.setSrcFileName(filesFileName);
				uploadHandle.setUploadDir(uploadDir);
				uploadHandle.setAllowedTypes(fileAllowedTypes);
				uploadHandle.setMaxSize(fileMaxSize);
				uploadHandle.upload();
				fileBean.setNewName(uploadHandle.getDestFileName());
				fileBean.setOldName(uploadHandle.getSrcFileName());
				long sizefil = uploadHandle.getSrcFile().length()/1024;
				fileBean.setSize(sizefil == 0 ? 1 : sizefil);
				fileBean.setSuffix(uploadHandle.getSuffix());
				fileBean.setUrl(uploadHandle.getRealPathDir());

			}
		} catch (Exception e) {
			e.printStackTrace();
			// 将文件上传的异常传换成应用异常。
			throw new RuntimeException(e);
		}
		return fileBean;
	}
	

	/**
	 * 开始处理上传文件
	 */
	private void upload() {
		if(srcFile==null){
			throw new RuntimeException("上传文件失败：找不到源文件srcFile＝"+srcFile);
		}
		if(StringUtils.isBlank(srcFileName)){
			throw new RuntimeException("上传文件失败：找不到源文件名srcFileName＝"+srcFileName);
		}
		if(StringUtils.isBlank(uploadDir)){
			throw new RuntimeException("上传文件失败：找不到上传目录路径uploadDir＝"+uploadDir);
		}
		
		//验证是否支持该文件上传
		suffix=srcFileName.substring(srcFileName.lastIndexOf(".")+1);
		if(StringUtils.isNotBlank(allowedTypes)){
			String[] array=allowedTypes.split(",");
			boolean allowed=false;
			for (int i = 0; i < array.length; i++) {
				if(suffix.equalsIgnoreCase(array[i].trim())){
					allowed=true;
				}
			}
			if(!allowed){
				throw new RuntimeException("不支持上传的文件类型！");
			}
		}
		//验证文件大小是否超过设定的大小
		if(maxSize>0&&srcFile.length()>maxSize*1024){
			throw new RuntimeException("上传文件太大！");
		}
		
		try{
			this.parseUploadDir();
			this.initDestFileName();
			InputStream is = null;
			OutputStream ops = null;
			try {
				is = new FileInputStream(srcFile);
				File destFile = new File(realPathDir, destFileName);
				ops = new FileOutputStream(destFile);
				byte[] b = new byte[1024];
				int length = 0;
				while ((length = is.read(b)) > 0) {
					ops.write(b, 0, length);
				}
			} catch (Exception ex) {
				throw new RuntimeException("上传文件出错！",ex);
			} finally {
				if(is!=null)
					is.close();
				if(ops!=null)
					ops.close();
			}
		}catch(Exception e){
			throw new RuntimeException(e);
		}
	}
	
	/**
	 * 解析上传路径
	 * 文件按年>月>周 目录结构存放
	 */
	private synchronized void parseUploadDir() throws Exception {
		Calendar today=Calendar.getInstance();
		String year=DateUtil.formatDate(today.getTime(),"yyyy");
		String month=DateUtil.formatDate(today.getTime(),"MM");
		String day = DateUtil.formatDate(today.getTime(),"dd");
		String week=String.valueOf(today.get(Calendar.WEEK_OF_MONTH));
		//realPathDir=uploadDir+year+"/"+month+"/"+day+"/";
		realPathDir=uploadDir;
		File realPath=new File(realPathDir);
		if(!realPath.exists()){
			realPath.mkdirs();
		}
	}
	


	/**
	 * 目标文件名
	 */
	private void initDestFileName() {
		//不需更改文件名
		destFileName=srcFileName;
		//需要更改文件名
		if(YES.equals(needChangeFileName)){
			int index = srcFileName.lastIndexOf(".");
			String header = srcFileName;
			if(index>0){
				header = srcFileName.substring(0, index);
			}
			destFileName=header+generateRamdomName()+"."+suffix;
		}
		//如果当前目录下出现重名，则更改
		else if(AUTO.equals(needChangeFileName)){
			File dir=new File(realPathDir);
			if(dir==null){
				throw new RuntimeException("附件目录不存在，请新建"+realPathDir);
			}
			File[] fileList=dir.listFiles();
			for (File file : fileList) {
				if(file.getName().equalsIgnoreCase(srcFileName)){
					destFileName=generateRamdomName()+"."+suffix;
					break;
				}
			}
		}
	}
	
	/**
	 * 获取允许上传的文件类型
	 * @return
	 */
	public String getAllowedTypes() {
		return allowedTypes;
	}

	public void setAllowedTypes(String allowedTypes) {
		this.allowedTypes = allowedTypes;
	}
	
	/**
	 * 获取允许上传文件的最大值
	 * @return
	 */
	public long getMaxSize() {
		return maxSize;
	}

	public void setMaxSize(long maxSize) {
		this.maxSize = maxSize;
	}

	/**
	 * 生成随机文件名＝年年月日时分秒+四位随机数
	 * @return
	 */
	private String generateRamdomName(){
		String str=DateUtil.formatDate(new Date(), "yyyyMMddHHmmssSSS");
		return str;
	}

	/**
	 * 目标文件名
	 * @return
	 */
	public String getDestFileName() {
		return destFileName;
	}

	
	/**
	 * 实际文件路径
	 * @return
	 */
	public String getRealPathDir() {
		return realPathDir;
	}

	/**
	 * 文件后缀
	 * @return
	 */
	public String getSuffix() {
		return suffix;
	}

	/**
	 * 上传目录
	 * @return
	 */
	public String getUploadDir() {
		return uploadDir;
	}

	public String getSrcFileName() {
		return srcFileName;
	}

	public File getSrcFile() {
		return srcFile;
	}
	
}

